package struts.ext.base;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;

import struts.ext.annotation.Return;
import struts.ext.annotation.ReturnType;
import struts.ext.returntype.handler.DownloadHandler;
import struts.ext.returntype.handler.ExcelHandler;
import struts.ext.returntype.handler.JsonHandler;
import struts.ext.util.Jsons;


public class ExtDispatchAction extends DispatchAction {
	
	
	//方法的参数列表
	//原来的是{ ActionMapping.class, ActionForm.class, HttpServletRequest.class,
    //HttpServletResponse.class}
    protected Class<?>[] paramTypes = {ExtServletWrapper.class };
    
    protected final static String EXT_METHOD_CACHE_KEY_PREFIX="EXT_METHOD_CACHE_KEY_PREFIX";
    protected final static String EXT_DEFAULT_METHOD="defaultMethod";
    

    /**
     * 根据方法名得到具体的方法，先从缓存中读，没有的话就从clazz中得到，然后放入缓存中（HashMap）
     * @param name
     * @return
     * @throws NoSuchMethodException
     */
    @SuppressWarnings("unchecked")
	protected Method getMethodTD(String name)
        throws NoSuchMethodException {
    	//以示区别，在自家的方法前加上前缀作为key放入缓存中
    	String cacheKey=EXT_METHOD_CACHE_KEY_PREFIX+name;
    	
        synchronized (methods) {
            Method method = (Method) methods.get(cacheKey);

            if (method == null) {
                method = clazz.getMethod(name, paramTypes);
                methods.put(cacheKey, method);
            }
            return (method);
        }
    }
    
    protected ActionForward unspecified(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		return dispatchMethod(mapping, form, request, response, EXT_DEFAULT_METHOD);
	}
    
    protected ActionForward dispatchMethod(ActionMapping mapping,
        ActionForm form, HttpServletRequest request,
        HttpServletResponse response, String name)
        throws Exception {

        if (name == null) {
            //return this.unspecified(mapping, form, request, response);
        	name = EXT_DEFAULT_METHOD; //为空就执行默认的方法
        }

        Method method = null;
        
        
        boolean useCumtomMethod=false; //是否是使用了自定义的方法。
        try {
        	// 按传统方式取 action方法
            method = getMethod(name);
        } catch (NoSuchMethodException ex) {
        	try {
        	// 没取到的话 再按新方式取 action方法
        	 method = getMethodTD(name);
        	 // 标识一下到底是使用的原始方法 还是新方法
        	 useCumtomMethod=true;
	        } catch (NoSuchMethodException e) {
	            String message =
	                messages.getMessage("dispatch.method", mapping.getPath(), name);
	
	            log.error(message, e);
	
	            String userMsg =
	                messages.getMessage("dispatch.method.user", mapping.getPath());
	            throw new NoSuchMethodException(userMsg);
	        }
        }
        ActionForward forward = null;

        try {
        	Object retValue = null;
        	
        	if (!useCumtomMethod){ // 如果是原始 action 方法
        		Object[] args = { mapping, form, request, response };
        		//真正执行方法的地方：
        		retValue = method.invoke(this, args);

        	}else{
        		// 如果是新 action 方法
        		// TDServletWrapperFactory.getInstance创建一个封装的对象
        		Object[] args = { ExtServletWrapperFactory.getInstance(mapping, form,request, response) };
        		retValue = method.invoke(this, args);
        	}
        	
    		if (retValue  != null){
    			if (retValue  instanceof ActionForward){ //方法的返回值是ActionForward
    				forward=(ActionForward)retValue ;
        	    }else{
					//如果有Return的注释，且value是Json，那么就把返回值作为json数据传给前台
	    	        Return ret = method.getAnnotation(Return.class);
	    	        if(ret != null){
	    	        	if(ret.value() == ReturnType.String){ //String
	    	        		if(retValue instanceof String){
	    	        			forward = mapping.findForward(String.valueOf(retValue));
	    	        		}else{
	    	        			throw new Exception("方法[" + method.getName() + "]返回了不正确的类型[" + retValue .getClass().getCanonicalName()+"]，请返回String类型。");
	    	        		}
	    	        		
	    	        	}else if(ret.value() == ReturnType.Json){ //json
	    	        		forward = new JsonHandler().handle(request,response,retValue);
	    	        		
	    	        	}else if(ret.value() == ReturnType.Excel){//exel
	    	        		forward = new ExcelHandler().handle(request, response, retValue);
	    	        		
	    	        	}else if(ret.value() == ReturnType.DownLoad){
	    	        		forward = new DownloadHandler().handle(request, response, retValue);
	    	        		
	    	        	}else {
	    	        		throw new Exception("方法[" + method.getName() + "]返回了不正确的类型。");
	    	        	}
	    			}else if(retValue instanceof String){ //方法的返回值是String
	    				forward = mapping.findForward(String.valueOf(retValue ));
	    			}else{
	    	        	throw new Exception("方法[" + method.getName() + "]返回了不正确的类型[" + retValue .getClass().getCanonicalName()+"]，请返回ActionForward或者String类型。");
	    	        }
        	    }
    		}
        } catch (ClassCastException e) {
            String message =
                messages.getMessage("dispatch.return", mapping.getPath(), name);

            log.error(message, e);
            throw e;
        } catch (IllegalAccessException e) {
            String message =
                messages.getMessage("dispatch.error", mapping.getPath(), name);

            log.error(message, e);
            throw e;
        } catch (InvocationTargetException e) {

            Throwable t = e.getTargetException();

            if (t instanceof Exception) {
                throw ((Exception) t);
            } else {
                String message =
                    messages.getMessage("dispatch.error", mapping.getPath(),
                        name);
                log.error(message, e);
                throw new ServletException(t);
            }
        }
        return (forward);
    }
}
    

